// 12.1.1 虚成员函数
// 在模板类中，虚函数的处理有一些特殊的考虑点：

// 虚函数在模板类中的工作方式
// 模板类可以包含虚函数，就像普通类一样：
template <typename T>
class Base
{
public:
    virtual void interface()
    {
        // 默认实现
    }

    virtual T process(T const &value)
    {
        return value;
    }
};

// 虚函数的特点
// 1.每个模板实例化都有自己的虚函数表：
Base<int> *p1 = new Derived<int>;
Base<double> *p2 = new Derived<double>;
// 这里Base<int>和Base<double>是完全不同的类，有各自的虚函数表。

// 2.模板参数可以影响虚函数签名：
template <typename T>
class Base
{
public:
    virtual T process(T const &value);
};
// 不同的T会生成不同签名的虚函数。

// 3.协变返回类型：虚函数的返回类型可以是派生类型，这在模板中同样适用。

// 非模板类的虚函数表结构
// 对于非模板类，每个包含虚函数的类都有一个唯一的虚函数表（vtable）：
class Base
{
public:
    virtual void func1() { /*...*/ }
    virtual void func2() { /*...*/ }
};

class Derived : public Base
{
public:
    void func1() override { /*...*/ } // 覆盖基类的func1
};
// Base类有一个虚函数表，包含func1和func2的函数指针
// Derived类也有一个虚函数表，但其中func1的条目指向Derived::func1

// 与模板类的关键区别
// 与模板类相比，非模板类的虚函数表有以下特点：
// 1.单一虚函数表：
//      非模板类只有一个虚函数表，而不是每个模板实例化一个
//      所有该类的对象共享同一个虚函数表
// 2.继承关系明确：
Base *p1 = new Derived; // 有效，因为Derived继承自Base
// 这里只有一个Base类和一个Derived类，它们之间有明确的继承关系

// 虚函数签名固定：
// 非模板类的虚函数签名在编译时就完全确定
// 不会因为参数类型变化而产生不同版本的虚函数

// 内存布局对比
// 非模板类：
/**
 *
 *
Base对象:
[vptr] -> Base vtable:
           [Base::func1]
           [Base::func2]

Derived对象:
[vptr] -> Derived vtable:
           [Derived::func1]  // 覆盖了Base::func1
           [Base::func2]     // 继承自Base

Base<int>对象:
[vptr] -> Base<int> vtable:
           [Base<int>::func1]
           [Base<int>::process<int>]

Base<double>对象:
[vptr] -> Base<double> vtable:
           [Base<double>::func1]
           [Base<double>::process<double>]
 */
